
#include <drawables/animation.hpp>
#include "assert.h"
#include <window/window.hpp>

#include <boost/shared_array.hpp>
#include <string>
#include <fstream>

#include <utils/from_string.hpp>

void animation::update( float delta_time )
{
	if( m_frames.size() <= 1 )
		return;

	unsigned int old_frame = m_current_frame;

	m_frame_delta += delta_time;
	while( m_frame_delta >= m_frame_times[m_current_frame] )
	{
		m_frame_delta -= m_frame_times[m_current_frame];
		++m_current_frame;
		if( m_current_frame == m_frames.size() )
			m_current_frame = 0;
	}

	if( old_frame == m_current_frame )
		m_frames[old_frame]->update(delta_time);
	else
		m_frames[m_current_frame]->reset();
}

bool animation::addFrame( const std::string & filepath, bool flip_h )
{
	sprite * spr = new sprite(filepath);
	spr->flip_horizontal(flip_h);
	drawable_ptr dptr = drawable_ptr(spr);
	
	m_frames.push_back( dptr );
	return true;
}

bool animation::addFrame( const std::string & filepath, int top, int left, int bottom, int right, bool flip_h )
{
	frame_sprite* fs = new frame_sprite();
	fs->m_rect = sf::IntRect( left, top, right, bottom );
	fs->m_sprite = drawable_ptr( new sprite(filepath) );
	fs->m_flip = flip_h;

	drawable_ptr dptr = drawable_ptr(fs);
	
	m_frames.push_back( dptr );
	return true;
}

// TODO
bool animation::loadFromFile( const std::string & filepath )
{
	std::ifstream file( filepath.c_str( ) );

	if ( !file.is_open( ) )
	{
		throw std::runtime_error( filepath + " could not be opened." );
	}

	file.seekg( 0, std::ios::end );
	size_t length = (size_t)file.tellg( );
	file.seekg( 0, std::ios::beg );

	boost::shared_array< char > text( new char[ length + 1 ] );
	memset( text.get( ), 0, sizeof( char ) * ( length + 1 ) );

	file.read( text.get( ), length );
	rapidxml::xml_document<> document; document.parse<0>( text.get( ) );

	rapidxml::xml_node<> * animation_node = document.first_node( "animation" );
	if ( animation_node == nullptr )
		throw std::runtime_error( filepath + " is invalid." );
	
	return loadAnimation( animation_node );
}
bool animation::loadAnimation( rapidxml::xml_node<> const * const animation_node )
{
	float bfps = 8;
	float tpf = 0.125f;
	for (rapidxml::xml_attribute<> *attr = animation_node->first_attribute(); attr; attr = attr->next_attribute())
	{
		std::string attName = attr->name();
		if( attName == "base_fps" )
		{
			bfps = from_string< float >( attr->value( ) );
			m_time_per_frame = 1.f/bfps;
			tpf = m_time_per_frame;
		}
	}
	
	int frame_count = 0;

	for (rapidxml::xml_node<> * frame_node = animation_node->first_node( "frame" ); frame_node; frame_node = frame_node->next_sibling( "frame" ) )
	{
		rapidxml::xml_attribute<> *time_att = frame_node->first_attribute("time");
		if( time_att )
		{
			m_frame_times.push_back( from_string<float>(time_att->value()) );
		}
		else
		{
			m_frame_times.push_back( tpf );
		}

		rapidxml::xml_node<> * frame_type_node = frame_node->first_node();
		if( frame_type_node )
		{
			std::string ftname = frame_type_node->name();
			if( ftname == "sprite" )
			{
				bool flip = false;
				std::string fileP;
				float top =0; float left=0; float right=0; float bottom=0;
				bool bt = false; bool bl = false;
				for (rapidxml::xml_attribute<> *attr = frame_type_node->first_attribute(); attr; attr = attr->next_attribute())
				{
					std::string attName = attr->name();
					if( attName == "filepath" )
						fileP = attr->value();
					else if( attName == "top" )
					{
						top = from_string<float>(attr->value());
						bt = true;
					}
					else if( attName == "left" )
					{
						left = from_string<float>(attr->value());
						bl = true;
					}
					else if( attName == "bottom" )
						bottom = from_string<float>(attr->value());
					else if( attName == "right" )
						right = from_string<float>(attr->value());
					else if( attName == "width" && bl )
						bottom = from_string<float>(attr->value()) + top;
					else if( attName == "height" && bt )
						right = from_string<float>(attr->value()) + left;
					else if( attName == "flip_h" )
					{
						//if( attr->value() == "true" )
							flip = true;
					}
				}
				// dimensions take x,y,width height
				for (rapidxml::xml_node<> * d_node = frame_type_node->first_node( "dimensions" ); d_node; 
					d_node = d_node->next_sibling( "dimensions" ) )
				{
					Vector4 dims = from_string<Vector4>( d_node->value() );
					top = dims.y;
					left = dims.x;
					bottom = dims.y+dims.w;
					right = dims.x+dims.z;
				}

				if( top != bottom && left != right )
					addFrame(fileP, (int)top, (int)left, (int)bottom, (int)right,flip);
				else
					addFrame(fileP,flip);
				++frame_count;
			}
			else if( ftname == "animation" )
			{
				animation* anim = new animation() ;

				if( !anim->loadAnimation( frame_type_node ) )
					return false;
				addFrame( drawable_ptr( anim ) );
				++frame_count;
			}
		}
	}

	if( frame_count < 1 )
		return false;
	return true;
}
void animation::reset()
{
	m_current_frame = 0;
	m_frame_delta = 0;
}

bool animation::draw( float x, float y, float rotation, float scale )
{
	if( m_current_frame >= 0 && m_current_frame < m_frames.size() )
		m_frames[m_current_frame]->draw(m_position.x+x,m_position.y+y,m_rotation+rotation,m_scale*scale);

	return true;
}

animation::animation( const std::string & filepath )
{
	if (!loadFromFile( filepath ) )
	{
		throw std::runtime_error( "Could not construct animation from " + filepath );
	}
	
	reset( );
}

animation::~animation()
{
}